#include <cassert>
#include <iostream>
#include <map>
#include <deque>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include "server.h"

using namespace std;

class DeterministicPseudoRandomServer: public Server {
 public:
   enum ColType { INT, DOUBLE, BOOL };

   DeterministicPseudoRandomServer(int n): _n(n) {}

   // We need to print everything here, because we don't know amount
   // of output columns before consuming everything, and we don't know
   // when we finally consumed everything.
   ~DeterministicPseudoRandomServer() {
     int amount = 0;
     int columns = 0;

#ifndef NO_OUTPUT
     // check that amount of data in output columns is equal
     for (map<int, deque<int32_t> >::const_iterator i = _intQueue.begin(); i != _intQueue.end(); ++i) {
       if (amount == 0) {
         amount = i->second.size();
       } else {
         assert(amount == (int) i->second.size());
       }

       columns = max(columns, i->first + 1);
    }

     for (map<int, deque<double> >::const_iterator i = _doubleQueue.begin(); i != _doubleQueue.end(); ++i) {
       if (amount == 0) {
        amount = i->second.size();
       } else {
         assert(amount == (int) i->second.size());
       }

       columns = max(columns, i->first + 1);
     }

     for (map<int, deque<bool> >::const_iterator i = _boolQueue.begin(); i != _boolQueue.end(); ++i) {
       if (amount == 0) {
         amount = i->second.size();
       } else {
         assert(amount == (int) i->second.size());
       }

       columns = max(columns, i->first + 1);
     }

     // print everything in rows
     for (int i = 0; i < amount; ++i) {
       for (int col = 0; col < columns; ++col) {
         if (_intQueue.find(col) != _intQueue.end()) {
           cerr << setw(15) << _intQueue[col].front();
           _intQueue[col].pop_front();
         } else if (_doubleQueue.find(col) != _doubleQueue.end()) {
           cerr << setw(15) << _doubleQueue[col].front();
           _doubleQueue[col].pop_front();
         } else {
           assert(_boolQueue.find(col) != _boolQueue.end());
           cerr << setw(7) << boolalpha << _boolQueue[col].front();
           _boolQueue[col].pop_front();
         }
       }

       cerr << "\n";
     }
#endif
   }

   virtual int GetDoubles(int column_index, int number, double* destination) {
     initColumnIfNeeded(column_index, DOUBLE);

     int amount = min(number, _dataLeft[column_index]);

     for (int i = 0; i < amount; ++i) {
       destination[i] = normal(column_index);
     }

     _dataLeft[column_index] -= amount;
     return amount;
   }

   virtual int GetInts(int column_index, int number, int32_t* destination) {
     initColumnIfNeeded(column_index, INT);

     int amount = min(number, _dataLeft[column_index]);

     for (int i = 0; i < amount; ++i) {
       destination[i] = rand(column_index) % 123456;
     }

     _dataLeft[column_index] -= amount;
     return amount;
   }

   virtual int GetByteBools(int column_index, int number, bool* destination) {
     initColumnIfNeeded(column_index, BOOL);

     int amount = min(number, _dataLeft[column_index]);

     for (int i = 0; i < amount; ++i) {
       destination[i] = (bool) (rand(column_index) % 2);
     }

     _dataLeft[column_index] -= amount;
     return amount;
  }

   virtual int GetBitBools(int column_index, int number, char* destination) {
     assert(false); // NOT IMPLEMENTED
     return 0;
   }

   virtual void ConsumeDoubles(int column_index, int number, const double* destination) {
     checkOutputType(column_index, DOUBLE);

#ifndef NO_OUTPUT
     for (int i = 0; i < number; ++i) {
       _doubleQueue[column_index].push_back(destination[i]);
     }
#endif
   }

   virtual void ConsumeInts(int column_index, int number, const int32_t* destination) {
     checkOutputType(column_index, INT);

#ifndef NO_OUTPUT
     for (int i = 0; i < number; ++i) {
       _intQueue[column_index].push_back(destination[i]);
     }
#endif
   }

   virtual void ConsumeByteBools(int column_index, int number, const bool* destination) {
     checkOutputType(column_index, BOOL);

#ifndef NO_OUTPUT
     for (int i = 0; i < number; ++i) {
       _boolQueue[column_index].push_back(destination[i]);
     }
#endif
   }

   virtual void ConsumeBitBools(int column_index, int number, const char* destination) {
     assert(false); // NOT IMPLEMENTED
   }

 private:
   void initColumnIfNeeded(int column_index, ColType type) {
     if (_dataLeft.find(column_index) == _dataLeft.end()) {
       _dataLeft[column_index] = _n;
       _types[column_index] = type;
       _randomState[column_index] = column_index;
     }
   }

   void checkOutputType(int column_index, ColType type) {
     if (_outputTypes.find(column_index) == _outputTypes.end()) {
       _outputTypes[column_index] = type;
     } else {
       assert(_outputTypes[column_index] == type);
     }
   }

   int32_t rand(int32_t column_index) {
     int32_t state = _randomState[column_index];
     state = state * 1526089 + 319993 + (~state >> 15);
     _randomState[column_index] = state;
     return state;
   }

   // Returns a U(0, 1) variable.
   double uniform(int column_index) {
     return (double) (1 + (rand(column_index) % ((1 << 30) - 1))) / (double) (1 << 30);
   }

   // Returns a N(0, 1) variable using the Box-Muller method.
   double normal(int column_index) {
     double cc = uniform(column_index);
     return sqrt(-2. * log(abs(cc))) * cos(8 * atan(1) * cc);
   }

   int _n;
   map<int, int> _dataLeft;
   map<int, int32_t> _randomState;
   map<int, ColType> _types;
   map<int, ColType> _outputTypes;
   map<int, deque<int32_t> > _intQueue;
   map<int, deque<double> > _doubleQueue;
   map<int, deque<bool> > _boolQueue;
};

Server* CreateServer(int query_id, int data_size) {
 ios_base::sync_with_stdio(false);
 return new DeterministicPseudoRandomServer(data_size);
}
